package com.wing.common.utils;

import cn.hutool.core.util.StrUtil;
import com.wing.common.enums.Idempotent;
import com.wing.common.enums.RequestKeyParam;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Objects;
import java.util.regex.Pattern;

/**
 * @Author: zcg
 * @Description: 生成LockKey
 * @Date: 2024/3/12
 **/
public class RequestKeyGenerator {

    // IPv4地址的正则表达式模式
    private static final String IPV4_PATTERN =
            "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$";

    /**
     * 获取LockKey
     *
     * @param joinPoint 切入点
     * @return
     */
    public static String getLockKey(ProceedingJoinPoint joinPoint) {
        //获取连接点的方法签名对象
        MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
        //Method对象
        Method method = methodSignature.getMethod();
        //获取Method对象上的注解对象
        Idempotent idempotent = method.getAnnotation(Idempotent.class);
        //获取方法参数
        final Object[] args = joinPoint.getArgs();
        //获取Method对象上所有的注解
        final Parameter[] parameters = method.getParameters();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < parameters.length; i++) {
            final RequestKeyParam keyParam = parameters[i].getAnnotation(RequestKeyParam.class);
            //如果属性不是RequestKeyParam注解，则不处理
            if (keyParam == null) {
                continue;
            }
            //如果属性是RequestKeyParam注解，则拼接 连接符 "& + RequestKeyParam"
            sb.append(idempotent.delimiter()).append(args[i]);
        }
        //如果方法上没有加RequestKeyParam注解
        if (StrUtil.isEmpty(sb.toString())) {
            //获取方法上的多个注解（为什么是两层数组：因为第二层数组是只有一个元素的数组）
            final Annotation[][] parameterAnnotations = method.getParameterAnnotations();
            //循环注解
            for (int i = 0; i < parameterAnnotations.length; i++) {
                final Object object = args[i];
                //获取注解类中所有的属性字段
                final Field[] fields = object.getClass().getDeclaredFields();
                for (Field field : fields) {
                    //判断字段上是否有RequestKeyParam注解
                    final RequestKeyParam annotation = field.getAnnotation(RequestKeyParam.class);
                    //如果没有，跳过
                    if (annotation == null) {
                        continue;
                    }
                    //如果有，设置Accessible为true（为true时可以使用反射访问私有变量，否则不能访问私有变量）
                    field.setAccessible(true);
                    //如果属性是RequestKeyParam注解，则拼接 连接符" & + RequestKeyParam"
                    sb.append(idempotent.delimiter()).append(ReflectionUtils.getField(field, object));
                }
            }
        }
        HttpServletRequest request = RequestUtils.getHttpServletRequest();
        String ip = RequestUtils.getIp(request);
        //返回指定前缀的key
        return idempotent.keyPrefix()+ idempotent.delimiter() + ip  + sb;
    }


    /**
     * 获取IP地址
     *
     * 使用Nginx等反向代理软件， 则不能通过request.getRemoteAddr()获取IP地址
     * 如果使用了多级反向代理的话，X-Forwarded-For的值并不止一个，而是一串IP地址，X-Forwarded-For中第一个非unknown的有效IP字符串，则为真实IP地址
     */
    public static String getIpAddr(HttpServletRequest request) {

        String ip = request.getHeader("x-forwarded-for");
        if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (StringUtils.isEmpty(ip) || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
            if ("127.0.0.1".equals(ip)) {
                //根据网卡取本机配置的IP
                InetAddress inet = null;
                try {
                    inet = InetAddress.getLocalHost();
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                }
                ip = inet.getHostAddress();
            }
        }

        //使用代理，则获取第一个IP地址
        if(StringUtils.isEmpty(ip) && ip.length() > 15) {
            if(ip.indexOf(",") > 0) {
                ip = ip.substring(0, ip.indexOf(","));
            }
        }

        if("0:0:0:0:0:0:0:1".equals(ip)){
            ip = "127.0.0.1";
        }

        return ip;
    }

    /**
     * 从URL中提取IP地址（如果主机是IP的话）
     * @param url 输入的URL字符串
     * @return 如果URL中的主机是IP地址则返回该IP，否则返回null
     */
    public static String extractIpIfHostIsIp(String url) {
        String host = extractHostFromUrl(url);
        if (host != null && isValidIPv4(host)) {
            return host;
        }
        return null;
    }

    /**
     * 从URL中提取主机部分（域名或IP）
     * @param url 输入的URL字符串
     * @return 主机部分字符串，如果无法提取则返回null
     */
    public static String extractHostFromUrl(String url) {
        if (url == null || url.isEmpty()) {
            return null;
        }

        // 移除协议部分
        String tempUrl = url;
        if (tempUrl.startsWith("http://")) {
            tempUrl = tempUrl.substring(7);
        } else if (tempUrl.startsWith("https://")) {
            tempUrl = tempUrl.substring(8);
        }

        // 移除端口和路径部分
        int pathIndex = tempUrl.indexOf('/');
        if (pathIndex != -1) {
            tempUrl = tempUrl.substring(0, pathIndex);
        }

        // 移除端口号
        int portIndex = tempUrl.indexOf(':');
        if (portIndex != -1) {
            tempUrl = tempUrl.substring(0, portIndex);
        }

        return tempUrl;
    }

    /**
     * 验证字符串是否是合法的IPv4地址
     * @param input 待验证的字符串
     * @return 如果是合法的IPv4地址返回true，否则返回false
     */
    public static boolean isValidIPv4(String input) {
        return IPV4_PATTERN_COMPILED.matcher(input).matches();
    }

    private static final Pattern IPV4_PATTERN_COMPILED = Pattern.compile(IPV4_PATTERN);


}

